#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <mutex>

#include <boost/algorithm/string.hpp>
#include "cppjieba/Jieba.hpp"
#include "log.hpp"

extern Log server_log;

namespace qyc
{
    // 网页信息
    typedef struct doc_info
    {
        std::string _title; // 网页标题
        std::string _context;  // 网页正文
        std::string _url;   // 网页url
    }doc_info_t;

    class file
    {
    public:
        static bool read(const std::string& path, std::string& out)
        {
            std::ifstream fin;
            fin.open(path, std::ifstream::in);
            if (!fin.is_open())
            {
                // std::cerr << path << " open err" << std::endl;
                server_log(Fatal, "%s open err, err file: %s, err line: %s", path.c_str(), __FILE__, __LINE__);
                return false;
            }
            
            std::string line;
            while (std::getline(fin, line))
                out += line;

            fin.close();
            return true;
        }
    };

    class string_html
    {
    public:
        // html中字段分隔符
        const static char html_segment_sep = '\3';
        // html间隔符
        const static char html_sep = '\n';
    public:
        static bool parser_title(const std::string& file_info, std::string& title)
        {
            size_t begin = file_info.find("<title>");
            if (begin == std::string::npos)
                return false;
            begin += std::string("<title>").size();
            size_t end = file_info.find("</title>", begin);
            if (end == std::string::npos)
                return false;
            
            title = file_info.substr(begin, end - begin);
            return true;
        }
        static bool parser_context(const std::string& file_info, std::string& context)
        {
            enum status
            {
                LABLE,
                CONTEXT
            };
            enum status st = LABLE;
            context.clear();
            for (char c : file_info)
            {
                switch (st)
                {
                case LABLE:
                    // 遇到'>'就是内容开始
                    if (c == '>')
                        st = CONTEXT;
                    break;
                case CONTEXT:
                    // 遇到'<'就是标签开始
                    if (c == '<')
                        st = LABLE;
                    else
                    {
                        if (c == '\n')
                            c = ' ';
                        context += c;
                    }
                    break;
                default:
                    break;
                }
            }

            return true;
        }
        static bool parser_url(const std::string& file_path, const std::string& dir_path, std::string& url)
        {
            // url_head
            url = "https://www.boost.org/doc/libs/1_84_0/doc/html";
            // url_tail
            url += file_path.substr(dir_path.size());
            return true;
        }
        static std::string html_to_string(const doc_info_t& html)
        {
            return html._title + html_segment_sep + html._context + html_segment_sep + html._url + html_sep;
        }
        static bool string_to_html(const std::string& info, doc_info_t& html)
        {
            // size_t pos1 = info.find(html_segment_sep);
            // if (pos1 == std::string::npos)
            //     return false;
            // html._title = info.substr(0, pos1);
            // size_t pos2 = info.find(html_segment_sep, pos1 + 1);
            // if (pos2 == std::string::npos)
            //     return false;
            // html._context = info.substr(pos1 + 1, pos2 - pos1);
            // size_t pos3 = info.find(html_sep, pos2 + 1);
            // if (pos3 == std::string::npos)
            //     return false;
            // html._url = info.substr(pos2 + 1, pos3 - pos2);
            
            // 使用boost库函数split切分
            std::vector<std::string> v;
            // 将切分后的string放入vector中
            // v输出型参数vector   info要被切分的字符串 html_segment_sep切分标记的字符串    boost::token_compress_on/off对切分标识是否压缩
            boost::split(v, info, boost::is_any_of(std::string(1, html_segment_sep)), boost::token_compress_on);
            if (v.size() != 3)
                return false;
            html._title = std::move(v[0]);
            html._context = std::move(v[1]);
            html._url = std::move(v[2]);
            return true;
        }
    };

    static const char* const dict_path = "./dict/jieba.dict.utf8";
    static const char* const hmm_path = "./dict/hmm_model.utf8";
    static const char* const user_dict_path = "./dict/user.dict.utf8";
    static const char* const idf_path = "./dict/idf.utf8";
    static const char* const stop_word_path = "./dict/stop_words.utf8";
    class string_word
    {
    private:
        string_word()
            : _jieba(dict_path, hmm_path, user_dict_path, idf_path, stop_word_path)
        {
            init_stop_word();
        }
        string_word(const string_word&) = delete;
        string_word& operator=(const string_word&) = delete;

        void init_stop_word()
        {
            server_log(Info, "读取stop words");
            //读取暂停词,存入stop_words
            std::ifstream fin(stop_word_path);
            if (!fin.is_open())
                server_log(Fatal, "%s open err, err file: %s, err line: %s", stop_word_path, __FILE__, __LINE__);
            std::string line;
            while (std::getline(fin, line))
                stop_words.insert({line, true});
            fin.close();
            server_log(Info, "读取stop words完成");
        }
    public:
        static string_word* get_instance()
        {
            static std::mutex mtx;
            if (_instance_jieba == nullptr)
            {
                std::lock_guard<std::mutex> lg(mtx);
                if (_instance_jieba == nullptr)
                    _instance_jieba = new string_word;
            }
            return _instance_jieba;
        }
        static void cut_string(const std::string& message, std::vector<std::string>& words)
        {
            get_instance()->_jieba.CutForSearch(message, words);
            // 将words中出现的暂停词全部去除
            for (auto iter = words.begin(); iter != words.end(); )
            {
                auto it = get_instance()->stop_words.find(*iter);
                // 检测当前的word是否为暂停词
                if (it != get_instance()->stop_words.end())
                    iter = words.erase(iter);
                else
                    ++iter;
            }
        }
    private:
        static string_word* _instance_jieba;
        cppjieba::Jieba _jieba;
        std::unordered_map<std::string, bool> stop_words;
    };
    string_word* string_word::_instance_jieba = nullptr;
}